package duyd.jpa.training.service.jpa;

import java.util.Date;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import duyd.jpa.training.domain.Contact;
import duyd.jpa.training.domain.Contact_;
import duyd.jpa.training.service.ContactService;

@Repository
@Service("contactService")
@Transactional
public class ContactServiceImpl implements ContactService {

	@PersistenceContext
	EntityManager entityManager;

	private Log log = LogFactory.getLog(ContactServiceImpl.class);

	@Transactional(readOnly = true)
	public List<Contact> findAll() {
		return entityManager.createNamedQuery("contact.findAll", Contact.class)
				.getResultList();
	}

	@Transactional(readOnly = true)
	public List<Contact> findAllWithDetail() {
		return entityManager.createNamedQuery("contact.findAllWithDetail",
				Contact.class).getResultList();
	}

	@SuppressWarnings("unchecked")
	@Transactional(readOnly = true)
	public List<Contact> findByBirthDateNativeQuery(Date date) {
		log.info("Search contact by birthDate using Native Query: " + date);
		return entityManager.createNativeQuery(
				"SELECT * FROM CONTACT WHERE BIRTH_DATE = '"
						+ new java.sql.Date(date.getTime()) + "'",
				Contact.class).getResultList();
	}

	@Transactional(readOnly = true)
	public Contact findById(Long id) {
		return entityManager
				.createNamedQuery("contact.findById", Contact.class)
				.setParameter("id", id).getSingleResult();
	}

	public Contact save(Contact contact) {
		if (contact.getId() == null) {
			entityManager.persist(contact);
			log.info("New contact inserted: " + contact);
		} else {
			entityManager.merge(contact);
			log.info("Existing contact updated: " + contact);
		}
		return contact;
	}

	public void delete(Contact contact) {
		// Contact managedContact = entityManager.merge(contact);
		// entityManager.remove(managedContact);
		// log.info("Contact removed: " + contact);
		entityManager.createNativeQuery("DELETE FROM contact_tel_detail")
				.executeUpdate();
	}

	@Transactional(readOnly = true)
	public List<Contact> findByFirstNameAndLastNameCriteriaQuery(
			String firstName, String lastName) {
		log.info("Search contact by first name and last name using Criteria query: "
				+ firstName + " " + lastName);
		CriteriaBuilder cb = entityManager.getCriteriaBuilder();
		CriteriaQuery<Contact> criteriaQuery = cb.createQuery(Contact.class);
		Root<Contact> contactRoot = criteriaQuery.from(Contact.class);
		contactRoot.fetch(Contact_.tels, JoinType.LEFT);
		contactRoot.fetch(Contact_.hobbies, JoinType.LEFT);
		criteriaQuery.select(contactRoot).distinct(true);
		Predicate criteria = cb.conjunction();
		// First Name
		if (firstName != null) {
			Predicate p = cb.equal(contactRoot.get(Contact_.firstName),
					firstName);
			criteria = cb.and(criteria, p);
		}
		// Last Name
		if (lastName != null) {
			Predicate p = cb
					.equal(contactRoot.get(Contact_.lastName), lastName);
			criteria = cb.and(criteria, p);
		}
		criteriaQuery.where(criteria);
		List<Contact> result = entityManager.createQuery(criteriaQuery)
				.getResultList();
		return result;
	}

}
